// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Logic for generating valid stack states for native function calls.
//!
//! This implements a `StackAccessor` that generates random bytearrays of a user defined length. We
//! then use this ability to run the native functions with different bytearray lengths in the
//! generated synthesis binary.
use move_ir_natives::dispatch::{Result, StackAccessor};
use rand::{rngs::StdRng, Rng, SeedableRng};
use types::byte_array::ByteArray;

/// A wrapper around data used to generate random valid bytearrays that replicate the semantics of
/// a `StackAccessor`.
pub struct StackAccessorMocker {
    gen: StdRng,
    /// The current byte array. Set to `None` if there is no bytearray.
    curr_byte_array: Option<ByteArray>,
    /// The length of bytearrays that will be generated.
    pub hash_length: usize,
}

impl StackAccessorMocker {
    /// Builds a new stack accessor mocker. User is responsible for later setting the length of,
    /// and generating the underlying bytearray.
    pub fn new() -> Self {
        let seed: [u8; 32] = [0; 32];
        Self {
            gen: StdRng::from_seed(seed),
            hash_length: 1,
            curr_byte_array: None,
        }
    }

    /// Set the bytearray length that will be generated in calls to `next_bytearray`.
    pub fn set_hash_length(&mut self, len: usize) {
        self.hash_length = len;
    }

    /// Set the `curr_byte_array` field to a freshly generated bytearray.
    ///
    /// The user is responsible for calling this between subsequent calls to `get_byte_array` in
    /// the `StackAccessor` trait.
    pub fn next_bytearray(&mut self) {
        let bytes: Vec<u8> = (0..self.hash_length)
            .map(|_| self.gen.gen::<u8>())
            .collect();
        self.curr_byte_array = Some(ByteArray::new(bytes))
    }
}

impl Default for StackAccessorMocker {
    fn default() -> Self {
        Self::new()
    }
}

impl StackAccessor for &mut StackAccessorMocker {
    fn get_byte_array(&mut self) -> Result<ByteArray> {
        Ok(std::mem::replace(&mut self.curr_byte_array, None).unwrap())
    }
}
